// Copyright (c) 2012 SameGoal LLC.
// All Rights Reserved.
// Author: Andy Hochhaus <ahochhaus@samegoal.com>

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef NET_HTTP_SERVER_EVENT_EVENT_SERVER_H_
#define NET_HTTP_SERVER_EVENT_EVENT_SERVER_H_

#include <event2/bufferevent.h>
#include <event2/event.h>
#include <event2/listener.h>
#include <gflags/gflags.h>
#include <glog/logging.h>
#include <openssl/ssl.h>
#include <time.h>

#include <string>
#include <vector>

#include "base/basictypes.h"
#include "net/http/headers/helper.h"
#include "third_party/libevent/libevent.h"
#include "util/worker_pool/worker_pool.h"

// TODO(ahochhaus):
// * redirect handler support

namespace sg {
namespace net {
namespace http {
namespace server {
namespace event {

class Handler;
class Request;
typedef std::function<void(Request *req, void *arg)> Callback;

class EventServer {
 public:
  EventServer(const std::string &ip,
              const std::string &hostname,
              const std::string &port,
              const uint32 app_callback_pool_size);
  ~EventServer();

  std::string hostname() const;
  std::string port() const;
  SSL_CTX *sctx();

  void Serve();
  void Terminate();
  event_base_t *event_base();
  headers::Helper *headers_helper();
  void set_read_timeout(const timeval_t &timeout);
  void set_write_timeout(const timeval_t &timeout);
  void set_ssl_timeout(int64 timeout);

  void AddHandler(Handler *handler);
  void AddRedirect(const std::string &host, const std::string &port);
  void AddEncryptedRedirect(const std::string &host,
                            const std::string &port,
                            const std::string &crt,
                            const std::vector<std::string> &chain,
                            const std::string &key);
  void SetHeadersHelper(headers::Helper *helper);
  void EnableEncryption(const std::string &certificate,
                        const std::vector<std::string> &chain_list,
                        const std::string &private_key);

  void SetConnectionCloseHandler(Callback callback);

  // TODO(ahochhaus): Make these private
  // Everything below this line is internal and only made public to work
  // around implementation level issues.
  void DispatchRequest(void *arg);
  ::sg::util::worker_pool::WorkerPool app_callback_pool;

 private:
  const std::string host_, hostname_, port_;
  struct redirect {
    std::string host;
    std::string port;
    evconnlistener_t *listener;
  };
  std::vector<redirect> redirects_;
  event_base_t *event_base_;
  evconnlistener_t *listener_;
  std::vector<Handler *> handlers_;
  Callback close_cb_;
  headers::Helper *helper_;
  SSL_CTX *sctx_;
  timeval_t *read_timeout_, *write_timeout_, *rate_limit_tick_;
  ev_token_bucket_cfg *rate_limit_cfg_;
  int64 ssl_timeout_;

  static evutil_addrinfo_t *DnsLookup(const std::string &host,
                                      const std::string &port);
  static void Accept(evconnlistener_t *listener,
                     evutil_socket_t fd,
                     sockaddr_t *address,
                     int socklen,
                     void *ctx);
  static void AcceptError(evconnlistener_t *listener, void *ctx);

  DISALLOW_COPY_AND_ASSIGN(EventServer);
};

}  // namespace event
}  // namespace server
}  // namespace http
}  // namespace net
}  // namespace sg

#endif  // NET_HTTP_SERVER_EVENT_EVENT_SERVER_H_
